package com.happywynne.elasticsearchtravel.serviceimpl;

import com.alibaba.fastjson.JSON;
import com.github.javafaker.Faker;
import com.happywynne.elasticsearchtravel.enums.BinType;
import com.happywynne.elasticsearchtravel.pojo.Address;
import com.happywynne.elasticsearchtravel.pojo.BaseBean;
import com.happywynne.elasticsearchtravel.pojo.Driver;
import com.happywynne.elasticsearchtravel.pojo.Point;
import com.happywynne.elasticsearchtravel.pojo.Size;
import com.happywynne.elasticsearchtravel.pojo.TrashBin;
import com.happywynne.elasticsearchtravel.pojo.TrashBinTravel;
import com.happywynne.elasticsearchtravel.service.FakerService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author Wynne.Lu
 * @date 2021/1/22 00:10
 * @desc
 */
@Service
@Slf4j
public class FakerServiceImpl implements FakerService {

    private List<Province> provinces;
    private static final String FILE_NAME = "city.json";

    @PostConstruct
    public void init() {
        initProvinceAndRegion();
    }

    private void initProvinceAndRegion() {
        try {
            provinces = findProvincesFromJson(FILE_NAME);
        } catch (IOException e) {
            log.error("解析city.json 发生异常 : " + e.getMessage());
        }
    }

    @Override
    public TrashBinTravel genFakeData() {
        Faker faker = new Faker(new Locale("zh-CN"));

        Size size = fakeSize(faker);

        Point point = fakePoint(faker);

        Driver driver = fakeDriver(faker);

        TrashBin trashBin = fakeTrashBin(faker, size);

        return fakeTrashBinTravel(faker, point, driver, trashBin);

    }

    private TrashBinTravel fakeTrashBinTravel(Faker faker, Point point, Driver driver, TrashBin trashBin) {
        TrashBinTravel trashBinTravel = new TrashBinTravel();
        Address currentAddress = assembleAddress(faker, fakePoint(faker));
        trashBinTravel.setCurrentAddress(currentAddress);
        trashBinTravel.setBin(trashBin);
        trashBinTravel.setDriver(driver);
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        trashBinTravel.setTravelBeginTime(df.format(faker.date().past(1000, TimeUnit.DAYS)));

        Address copyAddress = new Address();
        BeanUtils.copyProperties(currentAddress, copyAddress, "point");
        copyAddress.setPoint(fakePoint(faker));

        for (int i = 0; i < faker.number().numberBetween(2, 8); i++) {
            trashBinTravel.getAddress().add(assembleAddress(faker, fakePoint(faker)));
        }
        trashBinTravel.getAddress().add(copyAddress);
        modelInitialize(trashBinTravel);
        return trashBinTravel;
    }

    private TrashBin fakeTrashBin(Faker faker, Size size) {
        TrashBin trashBin = new TrashBin();
        trashBin.setBinCode(UUID.randomUUID().toString());
        trashBin.setSize(size);
        trashBin.setWeight(faker.number().randomDouble(2, 1, 1000));
        trashBin.setBinType(BinType.getRandomEnum());
        modelInitialize(trashBin);
        return trashBin;
    }

    private Driver fakeDriver(Faker faker) {
        Driver driver = new Driver(faker.name().fullName(), faker.number().numberBetween(20, 60));
        modelInitialize(driver);
        return driver;
    }

    private Point fakePoint(Faker faker) {
        return new Point(
                faker.number().randomDouble(2, (long) 73.66, (long) 135.05),
                faker.number().randomDouble(2, (long) 3.86, (long) 53.55)
        );
    }

    private Size fakeSize(Faker faker) {
        return new Size(
                faker.number().numberBetween(10, 100),
                faker.number().numberBetween(10, 100),
                faker.number().numberBetween(20, 40)
        );
    }

    private Address assembleAddress(Faker faker, Point point) {
        Address address = new Address();
        Province province = provinces.get(faker.number().numberBetween(0, 31));
        address.setPoint(point);
        address.setProvinceCode(province.getProvince());
        City city = getRandomCity(province.getCities());
        address.setRegionCode(city.getCity());
        if (city.getDistricts().size() != 0) {
            address.setDistrict(getRandomDistricts(city.getDistricts()));
        }
        modelInitialize(address);
        return address;
    }

    private City getRandomCity(List<City> cities) {
        Random random = new Random();
        return cities.get(Math.abs(random.nextInt(cities.size())));
    }

    private String getRandomDistricts(List<String> districts) {
        Random random = new Random();
        return districts.get(Math.abs(random.nextInt(districts.size())));
    }

    private BaseBean modelInitialize(BaseBean bean) {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateTime = df.format(new Date());
        bean.setCreateDateTime(dateTime);
        bean.setModifyDateTime(dateTime);
        bean.setId(UUID.randomUUID().toString());

        return bean;
    }

    private List<Province> findProvincesFromJson(String file) throws IOException {
        return JSON.parseArray(readJsonFile(file), Province.class);
    }

    // 读取json文件
    private String readJsonFile(String fileName) throws IOException {
        String jsonStr = "";
        ClassPathResource resource = new ClassPathResource(fileName);
//        File jsonFile = resource.getInputStream();
//        FileReader fileReader = new FileReader(jsonFile);
//        Reader reader = new InputStreamReader(new FileInputStream(jsonFile), StandardCharsets.UTF_8);
        Reader reader = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8);
        int ch = 0;
        StringBuffer sb = new StringBuffer();
        while ((ch = reader.read()) != -1) {
            sb.append((char) ch);
        }
//        fileReader.close();
        reader.close();
        jsonStr = sb.toString();
        return jsonStr;
    }
}

@Data
class Province {
    private String province;

    private List<City> cities;
}

@Data
class City {
    private String city;

    private List<String> districts;
}
